/**
 * 	write on 2005-10-26 23:58:46.
 */
package org.nanhill.xmlro.sro;

import static org.nanhill.commons.lang.StringConstants.GT;
import static org.nanhill.commons.lang.StringConstants.LT;
import static org.nanhill.commons.lang.StringConstants.QUOT;
import static org.nanhill.commons.xml.XmlConstants.AMP;
import static org.nanhill.commons.xml.XmlConstants.AMP_ESCAPE_ARRAY;
import static org.nanhill.commons.xml.XmlConstants.APOS;
import static org.nanhill.commons.xml.XmlConstants.APOS_ESCAPE_ARRAY;
import static org.nanhill.commons.xml.XmlConstants.GT_ESCAPE_ARRAY;
import static org.nanhill.commons.xml.XmlConstants.LT_ESCAPE_ARRAY;
import static org.nanhill.commons.xml.XmlConstants.QUOT_ESCAPE_ARRAY;
import static org.nanhill.xmlro.sro.SroConstants.BASE64_END;
import static org.nanhill.xmlro.sro.SroConstants.BASE64_START;
import static org.nanhill.xmlro.sro.SroConstants.BOOLEAN_FALSE_NODE;
import static org.nanhill.xmlro.sro.SroConstants.BOOLEAN_TRUE_NODE;
import static org.nanhill.xmlro.sro.SroConstants.DATE_END;
import static org.nanhill.xmlro.sro.SroConstants.DATE_START;
import static org.nanhill.xmlro.sro.SroConstants.DOUBLE_END;
import static org.nanhill.xmlro.sro.SroConstants.DOUBLE_START;
import static org.nanhill.xmlro.sro.SroConstants.INT_END;
import static org.nanhill.xmlro.sro.SroConstants.INT_START;
import static org.nanhill.xmlro.sro.SroConstants.LIST_END;
import static org.nanhill.xmlro.sro.SroConstants.LIST_START;
import static org.nanhill.xmlro.sro.SroConstants.LONG_END;
import static org.nanhill.xmlro.sro.SroConstants.LONG_START;
import static org.nanhill.xmlro.sro.SroConstants.MAP_END;
import static org.nanhill.xmlro.sro.SroConstants.MAP_START;
import static org.nanhill.xmlro.sro.SroConstants.NULL_END;
import static org.nanhill.xmlro.sro.SroConstants.NULL_START;
import static org.nanhill.xmlro.sro.SroConstants.SIZE_END;
import static org.nanhill.xmlro.sro.SroConstants.SIZE_START;
import static org.nanhill.xmlro.sro.SroConstants.SRO_END;
import static org.nanhill.xmlro.sro.SroConstants.SRO_START;
import static org.nanhill.xmlro.sro.SroConstants.STRING_END;
import static org.nanhill.xmlro.sro.SroConstants.STRING_START;

import java.util.Date;

import org.nanhill.xmlro.AbstractXmlroOutput;
import org.nanhill.xmlro.Serializer;
import org.nanhill.xmlro.SerializerFactory;
import org.nanhill.xmlro.XmlroException;

/**
 * This class is not thread safe, use
 * <code>org.nanhill.xmlro.sro.SroTransport</code> only.
 * 
 * xml to object
 * 
 * @author alin
 * 
 */
public class SroOutput extends AbstractXmlroOutput {

	private SerializerFactory serializerFactory;

	public void setSerializerFactory(SerializerFactory serializerFactory) {
		this.serializerFactory = serializerFactory;
	}

	public SroOutput(StringBuilder builder) {
		super(builder);
	}

	public SroOutput(Object obj, StringBuilder builder) {
		super(obj, builder);
	}

	public SroOutput(Object obj) {
		super(obj);
	}

	public SroOutput() {
		super();
	}

	public void construct() throws XmlroException {
		this.writeHeader();
		this.writeObject(this.getParsed());
		this.writeFooter();
	}

	public void writeHeader() {
		// append(XML_HEADER);
		append(SRO_START);
	}

	public void writeFooter() {
		append(SRO_END);
	}

	/**
	 * Writes a double value to the stream. The double will be written with the
	 * following syntax:
	 * 
	 * <code><pre>
	 *                                                                                                                                                                                                       &lt;double&gt;value&lt;/double&gt;
	 * </pre></code>
	 * 
	 * @param value
	 *            the double value to write.
	 */
	public void writeDouble(double value) {
		print(DOUBLE_START);
		print(value);
		print(DOUBLE_END);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.gsp.io.GspOutput#writeBytes(byte[])
	 */
	public void writeBytes(byte[] v) {
		this.print(BASE64_START);
		this.printBase64(v);
		this.print(BASE64_END);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.gsp.io.GspOutput#writeNull()
	 */
	public void writeNull() {
		this.print(NULL_START);
		this.print(NULL_END);
	}

	/**
	 * Writes a boolean value to the stream. The boolean will be written with
	 * the following syntax:
	 * 
	 * <code><pre>
	 *                                                                                                                                                                                                       &lt;boolean&gt;0&lt;/boolean&gt;
	 *                                                                                                                                                                                                       &lt;boolean&gt;1&lt;/boolean&gt;
	 * </pre></code>
	 * 
	 * @param value
	 *            the boolean value to write.
	 */
	public void writeBoolean(boolean value) {
		if (value)
			this.print(BOOLEAN_TRUE_NODE);
		else
			this.print(BOOLEAN_FALSE_NODE);
	}

	/**
	 * Writes the list header to the stream. List writers will call
	 * <code>writeListBegin</code> followed by the list contents and then call
	 * <code>writeListEnd</code>.
	 * 
	 * <code><pre>
	 *                                                                                                                                                                                               &lt;array&gt;
	 *                                                                                                                                                                                               	&lt;data&gt;
	 *                                                                                                                                                                                               		&lt;int&gt;11&lt;/int&gt;
	 *                                                                                                                                                                                               		&lt;string&gt;alin&lt;/string&gt;
	 *                                                                                                                                                                                               	&lt;/data&gt;
	 *                                                                                                                                                                                               &lt;/array&gt;
	 * </pre></code>
	 */
	public void writeListBegin() {
		print(LIST_START);
	}

	public void writeListSize(int size) {
		print(SIZE_START);
		print(Integer.toString(size));
		print(SIZE_END);
	}

	/**
	 * Writes the tail of the list to the stream.
	 */
	public void writeListEnd() {
		print(LIST_END);
	}

	/**
	 * Writes a string value to the stream using UTF-8 encoding. The string will
	 * be written with the following syntax:
	 * 
	 * <code><pre>
	 *                                                                                                                                                                                                     &lt;string&gt;string-value&lt;/string&gt;
	 * </pre></code>
	 * 
	 * If the value is null, nothing will be written.
	 * 
	 * @param v
	 *            the string value to write.
	 */
	public void writeString(String v) {
		print(STRING_START);
		for (int i = 0; i < v.length(); i++) {
			char ch = v.charAt(i);
			switch (ch) {
			case LT:
				append(LT_ESCAPE_ARRAY);
				break;
			case GT:
				append(GT_ESCAPE_ARRAY);
				break;
			case AMP:
				append(AMP_ESCAPE_ARRAY);
				break;
			case QUOT:
				append(QUOT_ESCAPE_ARRAY);
				break;
			case APOS:
				append(APOS_ESCAPE_ARRAY);
				break;
			default:
				print(ch);
			}

		}
		print(STRING_END);
	}

	/**
	 * Writes an integer value to the stream. The integer will be written with
	 * the following syntax:
	 * 
	 * <code> 
	 * 	&lt;int&gt;int value&lt;/int&gt;
	 * </code>
	 * 
	 * @param value
	 *            the integer value to write.
	 */
	public void writeInt(int value) {
		print(INT_START);
		print(value);
		print(INT_END);
	}

	/**
	 * Writes a long value to the stream. The long will be written with the
	 * following syntax:
	 * 
	 * <code>
	 *     &lt;uint&gt;int value&lt;/uint&gt;
	 * </code>
	 * 
	 * @param value
	 *            the long value to write.
	 */
	public void writeLong(long value) {
		print(LONG_START);
		print(value);
		print(LONG_END);
	}

	/**
	 * Writes the map header to the stream. Map writers will call
	 * <code>writeMapBegin</code> followed by the map contents and then call
	 * <code>writeMapEnd</code>. For example:
	 * 
	 * <code>
	 *                                                     &lt;struct&gt;
	 *                                                         &lt;member name=&quot;name&quot;&gt;
	 *                                                           &lt;string&gt;alin&lt;/string&gt;
	 *                                                           &lt;/member&gt;
	 *                                                                     &lt;member name=&quot;age&quot;&gt;
	 *                                                                &lt;int&gt;23&lt;/int&gt;
	 *                                                                 &lt;/member&gt;
	 *                                                &lt;/struct&gt;
	 * </code>
	 * 
	 */
	public void writeMapBegin() {
		print(MAP_START);
	}

	/**
	 * Writes the tail of the map to the stream.
	 */
	public void writeMapEnd() {
		print(MAP_END);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.Output#writeByte(byte)
	 */
	public void writeChar(char value) {
		this.print(value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.Output#writeDate(java.util.Date)
	 */
	public void writeDate(Date date) {
		this.print(DATE_START);
		this.print(date.getTime());
		this.print(DATE_END);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.Output#writeShort(short)
	 */
	public void writeShort(short value) {
		this.writeInt(value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.Output#writeByte(byte)
	 */
	public void writeByte(byte value) {
		this.writeInt(value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.protocol.xmlro.Output#writeFloat(float)
	 */
	public void writeFloat(float v) {
		this.writeDouble(v);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.XmlroOutput#writeMapKeyValueSplit()
	 */
	public void writeMapKeyValueSplit() {
	}

	public void writeMapElementSplit() {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.XmlroOutput#writeMapKey()
	 */
	public void writeMapKey(String key) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.XmlroOutput#writeListElementSplit()
	 */
	public void writeListElementSplit() {

	}

	@Override
	protected Serializer getSerializer(Class clz) {
		return this.serializerFactory.getSerializer(clz);
	}

}
